/*
 * Copyright (C) 2020 The zfoo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 */

package org.ricks.orm;

import org.ricks.ioc.RicksContext;
import org.ricks.ioc.anno.Init;
import org.ricks.ioc.anno.PreDestroy;
import org.ricks.ioc.scheduler.SchedulerBus;
import org.ricks.ioc.utils.Logger;
import org.ricks.ioc.utils.StopWatch;
import org.ricks.orm.accessor.IAccessor;
import org.ricks.orm.cache.IEntityCache;
import org.ricks.orm.manager.IOrmManager;
import org.ricks.orm.model.IEntity;
import org.ricks.orm.query.IQuery;
import org.ricks.orm.query.IQueryBuilder;

/**
 * @author godotg
 */
public class OrmContext {


    private static OrmContext instance;

    private IAccessor accessor;

    private IQuery query;

    private IOrmManager ormManager;

    private volatile boolean stop = false;

    public static OrmContext getOrmContext() {
        return instance;
    }

    public static IAccessor getAccessor() {
        return instance.accessor;
    }

    public static <PK extends Comparable<PK>, E extends IEntity<PK>> IQueryBuilder<PK, E> getQuery(Class<E> entityClazz) {
        return instance.query.builder(entityClazz);
    }

    public static IOrmManager getOrmManager() {
        return instance.ormManager;
    }

    public static boolean isStop() {
        return instance.stop;
    }

    @Init(priority = 1)
    public void onApplicationStart () {
        var stopWatch = new StopWatch();
        OrmContext.instance = this;
        instance.accessor = RicksContext.me().getInstance(IAccessor.class);
        instance.query = RicksContext.me().getInstance(IQuery.class);
        instance.ormManager = RicksContext.me().getInstance(IOrmManager.class);
        try {
            instance.ormManager.initBefore();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        instance.ormManager.initAfter();

        Logger.info("Orm started successfully and cost [{}] seconds", stopWatch.costSeconds());
    }

    @PreDestroy(priority = 1)
    public void onApplicationStop () {
        shutdown();
    }

    public static synchronized void shutdown() {
        if (isStop()) {
            return;
        }
        instance.stop = true;
        SchedulerBus.shutdown();
        try {
            instance.ormManager
                    .getAllEntityCaches()
                    .forEach(IEntityCache::persistAllBlock);
            instance.ormManager.mongoClient().close();
        } catch (Exception e) {
            Logger.error("Failed to close the MongoClient database connection", e);
        }
        Logger.info("Orm shutdown gracefully.");
    }
}
